App.require(["canvasTool", "resource", "gradient", "pattern", "marker", "helpAnimation"], 
    function (canvasTool, resource, gradient, pattern, marker, helpAnimation) {
    App.prototype.defineLayer = function (name, obj) {
        obj.getDomObj = obj.getDomObj || function () {};
        obj.getImgBySrc = obj.getImgBySrc || function (src) {
            return this._getCanvas().resource.getImage(src);
        };
        obj.getGradientByStr = obj.getGradientByStr || function (str, target) {
            var gradient = this._getCanvas().gradient;
            return gradient.gradientMap[str] || gradient.createGradientByStr(str, target);
        };
        obj.getMarkerByStr = obj.getMarkerByStr || function (str, target, ratio) {
            var marker = this._getCanvas().marker;
            return marker.markerObj[str] || marker.createMarkerByStr(str, target, ratio);
        };
        obj.pushImageSrc = obj.pushImageSrc || function (src) {
            this._getCanvas().resource.addImage(src);
        }
        obj.setAnimationObj = obj.setAnimationObj || function (obj) {
            this._getCanvas().animation.add(obj);
        };
        obj.setGradient = obj.setGradient || function (obj) {
            this._getCanvas().gradient.addGradient(obj);
        };
        obj.setPattern = obj.setPattern || function (obj) {
            this._getCanvas().pattern.addPattern(obj);
        };
        obj.setMarkerOption = function (id) {
            this._getCanvas().marker.add(id);
        };
        obj.getPatternByStr = obj.getPatternByStr || function (str, target) {
            var pattern = this._getCanvas().pattern;
            return pattern.patternMap[str] || pattern.createPatternByStr(str, target);
        };
        obj.addPropertyAnimation = obj.addPropertyAnimation || function (str, target) {
            this._getCanvas().animation.addPropertyAnimation(str, target);
        };
        obj.draw = obj.draw || function (ctx, ratio, option, isWrite) {
            option = option || {};
            ratio = ratio || 1;
            var isChange = false;
            if (this.property.hidden) return;
            ctx.save();
            if (typeof this.drawSelf === "function") {
                isChange = this.drawSelf(ctx, ratio);
            }
            var components = this.components;
            for (var i = 0; i < components.length; i++) {
                var com = components[i];
                if (typeof com.draw === "function") {
                    var comProperty = {};
                    com.data.isInstance = true;
                    for (var key in option) {
                        comProperty[key] = com.property[key];
                        if (isWrite) com[key] = option[key] || com.property[key];
                        else com[key] = com.property[key] || option[key];
                    }
                    com.draw(ctx, ratio, option, isWrite);
                    for (var key in comProperty) {
                        com[key] = comProperty[key];
                    }
                    com.data.isInstance = false;
                }
            }
            this.data.isChange = !!isChange;
            if (isChange) {
                var that = this;
                setTimeout(function () {
                    that.addChange();
                }, 0);
            }
            ctx.restore();
        };
        obj.getParentView = function () {
            var ptn = this.parent.tagName.toUpperCase();
            if (ptn === "STR-CANVAS" || ptn === "STR-LAYER") return this.parent;
            return this.parent.getPatternByStr();
        };
        obj.getFillOrStrokeStyle = function (str) {
            if (str.constructor.name == "CanvasGradient") return str;
            if (typeof str === "object") return "rgba(" + [str.r, str.g, str.b, str.a].join(", ") + ")";
            if (canvasTool.testColor(str)) return str;
            if (str.indexOf("$") == 0) {
                var pattern = this.getPatternByStr(str, this);
                return pattern;
            } else if (str.indexOf("#") == 0) {
                var gradient = this.getGradientByStr(str, this);
                return gradient;
            }
            return str;
        };
        obj._getCanvas = function () {
            if (this.tagName.toUpperCase() === "STR-CANVAS") return this;
            return this.parent._getCanvas();
        };
        // name duration delay repeat
        obj._createAnimation = function (animationStr) {
            var pattern = /([a-zA-Z\-0-9]+)\s+([0-9\.]+m?s)(\s+[0-9\.]+m?s)?([\s\S]+)?/;
            var match = pattern.exec(animationStr);
            if (match) {
                var obj = {
                    times: match[4] ? this.computedTimes(match[4]) : 1,
                    name: match[1].replace(/s/g, ""),
                    duration: this.computedDuration(match[2])
                };
                if (match[3]) {
                    obj.delay = this.computedDuration(match[3]);
                } else {
                    obj.delay = 0;
                }
                return obj;
            }
            return null;
        };
        obj.computedTimes = function (str) {
            str = str.replace(/\s+/g, "");
            if (str === "infinite") return Infinity;
            var matchInt = /\d+/.exec(str);
            if (matchInt) {
                return parseInt(matchInt[1]);
            }
            return 1;
        };
        obj.computedTransition = function (transition) {
            var pattern = /([a-z\-0-9A-Z]+\s+)?([0-9\.]+m?s)(\s+[0-9\.]+m?s)?/;
            var match = pattern.exec(transition);
            if (match) {
                var obj = {
                    duration: this.computedDuration(match[2])
                };
                if (match[1]) obj.animationType = match[1].replace(/\s+/g, "");
                else obj.animationType = "ease";

                if (match[3]) obj.delay = this.computedDuration(match[3]);
                else obj.delay = 0;
                return obj;
            }
            return null;
        };
        obj.computedDuration = function (str) {
            if (str.indexOf("ms") !== -1) return parseInt(str);
            return parseFloat(str) * 1000;
        };
        obj.addChange = function (changeObj, originObj, obj) {
            var parentCanvas = this._getCanvas();
            var target = originObj || this.property;
            if (obj || this.property.transition) {
                var tObj = this.computedTransition(this.property.transition) || {};
                if (obj) {
                    for (var key in obj) {
                        tObj[key] = obj[key];
                    }
                }
                if (tObj.duration) {
                    var oldObj = {};
                    for (key in changeObj) {
                        oldObj[key] = target[key];
                    }
                    var durationObj = canvasTool.createDurationObj(oldObj, changeObj, 0);
                    this._duration = durationObj;
                    parentCanvas.addChangeProperty({
                        type: "animation",
                        target: target,
                        animationObj:durationObj,
                        old: oldObj,
                        current: changeObj,
                        duration: tObj.duration,
                        delay: tObj.delay || 0,
                        animationType: tObj.animationType || "ease",
                        time: 1,
                        component: this,
                        emitEvent: "transitionend",
                        startEvent: "transitionstart",
                    })
                } else {
                    for (var key in changeObj) {
                        target[key] = changeObj[key];
                    }
                    parentCanvas.addChangeProperty({
                        type: "instance",
                        component: parentCanvas
                    });
                }
            } else {
                if (!changeObj && !originObj) {
                    parentCanvas.addChangeProperty({
                        type: "instance",
                        target: parentCanvas
                    });
                } else {
                    for (var key in changeObj) {
                        target[key] = changeObj[key];
                    }
                    parentCanvas.addChangeProperty({
                        type: "instance",
                        component: parentCanvas
                    });
                }
            }
        };
        obj.setValue = function (key, value) {
            var parentCanvas = this._getCanvas();
            if (this.property[key] === value) return false;
            if (key === "fill" && key === "stroke") {
                parentCanvas.removeFillOrStrokeByValue(this.property[key], this);
            } else if (key === "marker") {
                parentCanvas.removeMarkerByValue(this.property.marker, this);
            } else if (key === "src") {
                this.pushImageSrc(value);
            } else if (key === "animation") {
                parentCanvas.removePropertyAnimation(this.data.animation, this);
            }
            this.data.isChange = true;
            var marker = this._getMarkerParent();
            if (marker) marker.resetOption();
            var obj = {};
            obj[key] = value;
            if (this.data.isInstance) {
                this.property[key] = value;
            } else {
                this.addChange(obj);
            }

            return true;
        };
        obj._getMarkerParent = function () {
            if (this.parent.tagName.toUpperCase() === "STR-CANVAS") return false;
            if (this.parent.tagName.toUpperCase() === "STR-MARKER-LAYER") return this.parent;
            return this.parent._getMarkerParent();
        };
        obj.getValue = function (key) {
            return canvasTool.strToLayerType(key, this.property[key], this);
        };
        obj._getChange = function () {
            if (this.data.isChange) return true;
            var components = this.components;
            for (var i = 0; i < components.length; i++) {
                if (components[i]._getChange()) return true;
            }
            return false;
        };
        obj.createOffsetObj = function (offset, ratio, obj) {
            obj = obj || {};
            ratio = ratio || 1;
            if (typeof offset === "string") offset = canvasTool.createOffsetObj(offset);
            obj.x = offset.x * ratio;
            obj.y = offset.y * ratio;
            return obj
        };
        obj.computeFont = function (fontObj, ratio) {
            if (typeof fontObj === "object") return fontObj.size * ratio + "px " + fontObj.font; 
            return fontObj.replace(/\d+/g, function (match) {
                return parseFloat(match) * ratio + "";
            });
        };
        obj.containsBounds = obj.containsBounds || function (ctx, x, y, ratio) {
            ratio = ratio || 1;
            if (typeof this.createPath === "function") {
                this.createPath(ctx, ratio);
                if (ctx.isPointInPath(x * ratio, y * ratio)) return true;
            }
            return false;
        };
        obj.moveTravaseTree = obj.moveTravaseTree || function (ev, ctx, ratio) {
            ratio = ratio || 1;
            if (this.tagName.toUpperCase() === "STR-RENDER-LAYER" || this.tagName.toUpperCase() === "STR-MARKER-LAYER") return;
            var components = this.components;
            for (var i = 0; i < components.length; i++) {
                var com = components[i];
                if (com.property.hidden) continue;
                ctx.save();
                if (typeof com.transformView === "function") com.transformView(ctx, ratio);
                if (typeof com.moveLayerHandler === "function") com.moveLayerHandler(ev, ctx, ratio);
                com.moveTravaseTree(ev, ctx, ratio);
                ctx.restore();
            }
        };
        obj._createPropertyStr = obj._createPropertyStr || function (property) {
            var attrs = [];
            for (var key in property) {
                if (key !== "id") attrs.push(key + '="' + property[key] + '"');
            }
            return attrs.join(" ");
        };
        obj.clickTravaseTree = obj.clickTravaseTree || function (ev, ctx, ratio) {
            ratio = ratio || 1;
            if (this.tagName.toUpperCase() === "STR-RENDER-LAYER" || this.tagName.toUpperCase() === "STR-MARKER-LAYER") return;
            var components = this.components;
            for (var i = 0; i < components.length; i++) {
                var com = components[i];
                if (com.property.hidden) continue;
                ctx.save();
                if (typeof com.transformView === "function") com.transformView(ctx, ratio);
                if (typeof com.clickLayerHandler === "function") com.clickLayerHandler(ev, ctx, ratio);
                com.clickTravaseTree(ev, ctx, ratio);
                ctx.restore();
            }
        };
        obj.mousedownTravaseTree = obj.mousedownTravaseTree || function (ev, ctx, ratio) {
            ratio = ratio || 1;
            if (this.tagName.toUpperCase() === "STR-RENDER-LAYER" || this.tagName.toUpperCase() === "STR-MARKER-LAYER") return;
            var components = this.components;
            for (var i = 0; i < components.length; i++) {
                var com = components[i];
                if (com.property.hidden) continue;
                ctx.save();
                if (typeof com.transformView === "function") com.transformView(ctx, ratio);
                if (typeof com.mouseDownLayerHandler === "function") com.mouseDownLayerHandler(ev, ctx, ratio);
                com.mousedownTravaseTree(ev, ctx, ratio);
                ctx.restore();
            }
        };
        obj.removeAnimation = obj.removeAnimation || function () {
            this._getCanvas().animation.removeAnimationByTarget(this);
        };
        obj.transformView = obj.transformView || function (ctx, ratio) {
            ratio = ratio || 1;
            var obj = this.getPropertyUse(["transform"]);
            if (obj.transform) {
                var tList = obj.transform;
                for (var i = 0; i < tList.length; i++) {
                    var tObj = tList[i];
                    if (tObj.action == "translate") {
                        ctx.translate(tObj.args[0] * ratio, tObj.args[1] * ratio);
                    }
                    else {
                        ctx[tObj.action].apply(ctx, tObj.args);
                    }
                    
                }
            }
        };
        obj.useShadow = function (ctx, sObj, ratio) {
            if (typeof sObj === "object") {
                ratio = ratio || 1;
                var color = sObj.color;
                ctx.shadowOffsetX = sObj.x * ratio;
                ctx.shadowOffsetY = sObj.y * ratio;
                ctx.shadowBlur = sObj.blur * ratio;
                ctx.shadowColor = typeof color === "string" ? color : "rgba(" + [
                    color.r, color.g, color.b, color.a
                ].join(", ") + ")";
            }
            else {
                var sObj = canvasTool.changeShadowStr(sObj, ratio);
                if (sObj) {
                    var color = sObj.color;
                    ctx.shadowOffsetX = sObj.x;
                    ctx.shadowOffsetY = sObj.y;
                    ctx.shadowBlur = sObj.blur;
                    ctx.shadowColor = typeof color === "string" ? color : "rgba(" + [
                        color.r, color.g, color.b, color.a
                    ].join(", ") + ")";
                }
            }
        };
        obj.useLineDash = function (ctx, linedash, linedashoffset, ratio) {
            ctx.lineDashOffset = (linedashoffset || 0) * ratio;
            ctx.setLineDash(linedash.map(function (item) {
                return item * ratio;
            }));
        };
        obj.getPropertyUse = function (pList) {
            var result = {};
            for (var i = 0; i < pList.length; i++) {
                var key = pList[i];
                result[key] = canvasTool.getProperty(key, this);
            }
            return result;
        };
        this.defineComponent(name, obj);
    }

    App.define("strlayer", {
        Resource: resource.resource,
        Gradient: gradient.gradient,
        Pattern: pattern.pattern,
        Marker: marker.marker,
        Animation: helpAnimation.animation
    });
})